import { Injectable, Req, ExecutionContext } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { Like, Repository, EntityManager, Between } from 'typeorm';
import { instanceToPlain, plainToInstance } from 'class-transformer';
import { genSalt } from 'bcryptjs';

import { EquipmentEntity } from './entities/equipment.entity'

import { ResultData } from '../common/utils/result'
import { dateTimeSqlQuery } from '../common/utils/utils'
import { AppHttpCode } from '../common/enums/code.enum'

import { CreateEquipmentDto } from './dto/create-equipment.dto'
import { UpdateEquipmentDto } from './dto/update-equipment.dto'
import { UpdateEquipmentStatusDto } from './dto/update-equipment-status.dto'
import { FindEquipmentListDto } from './dto/find-equipment-list.dto'

import { OperalogService } from 'src/system/operalog/operalog.service'
import { LogisticsService } from 'src/common/logistics/logistics.service'

@Injectable()
export class EquipmentService {
  constructor(
    @InjectRepository(EquipmentEntity)
    private readonly equipmentRepo: Repository<EquipmentEntity>,
    @InjectEntityManager()
    private readonly equipmentManager: EntityManager,
    private readonly operalogService: OperalogService,
    private readonly logisticsService: LogisticsService,
  ) {}

  async findOneById(id: string): Promise<EquipmentEntity> {
    let equipment = await this.equipmentRepo.findOne({ where: { id } })
    equipment = plainToInstance(EquipmentEntity, { ...equipment }, { enableImplicitConversion: true })
    return equipment
  }

  /** 创建硬件 */
  async create(createEquipmentDto: CreateEquipmentDto, account: String, ip: String) {
    // 防止重复创建
    const salt = await genSalt()
    const equipment = plainToInstance(EquipmentEntity, { salt, ...createEquipmentDto }, { ignoreDecorators: true })
    const result = await this.equipmentManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<EquipmentEntity>(equipment)
    })

    if (!result) ResultData.fail(AppHttpCode.SERVICE_ERROR, '创建失败，请稍后重试');

    /** 创建硬件成功后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '硬件',
      operaModule: `添加硬件 -【${createEquipmentDto.name}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok(instanceToPlain(result));
  }

  /** 查询硬件分页查询 */
  async findList(dto: FindEquipmentListDto) {
    const { page, size, name, customerId, customerName, supplierName, technologyChargePerson, purchaseChargePerson, examinePerson, type, orderDate, phone } = dto;
    let queryDate;

    if (orderDate) {
      let sDate = `${orderDate}.000000`;
      let eDate = `${orderDate}`;
      let endDateData = dateTimeSqlQuery(eDate)
      endDateData = `${orderDate}.999999`;
      queryDate = Between(new Date(sDate), new Date(endDateData))
    } else {
      queryDate = null
    }

    const where = {
      ...(name ? { name: Like(`%${name}%`) } : null),
      ...(customerId ?  { customerId: customerId } : null),
      ...(customerName ? { customerName: Like(`%${customerName}%`) } : null),
      ...(supplierName ? { supplierName: Like(`%${supplierName}%`) } : null),
      ...(technologyChargePerson ? { technologyChargePerson: Like(`%${technologyChargePerson}%`) } : null),
      ...(purchaseChargePerson ? { purchaseChargePerson: Like(`%${purchaseChargePerson}%`) } : null),
      ...(examinePerson ? { examinePerson: Like(`%${examinePerson}%`) } : null),
      ...(type ? { type: type } : null),
      ...(phone ? { phone: Like(`%${phone}%`) } : null),
      ...(orderDate ? { orderDate: queryDate } : null),
      isDelete: 1
    }
    const equipment = await this.equipmentRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: size * (page - 1),
      take: size,
    })
    return ResultData.ok({ list: instanceToPlain(equipment[0]), total: equipment[1] })
  }

  /** 查询硬件信息 */
  async findOne(id: string) {
    const equipment = await this.findOneById(id)
    if (!equipment) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该硬件不存在或已删除')
    return ResultData.ok(instanceToPlain(equipment))
  }

  /** 查询硬件物流 */
  async findExpress(id: string) {
    const equipment = await this.findOneById(id)
    if (!equipment) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该硬件不存在或已删除')
    if (!equipment.courierNumber) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该硬件没有快递单号')
    if (!equipment.courierCompany) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该硬件没有选择快递公司')
    const expressInfo = await this.logisticsService.sendLogistics(equipment.courierCompany, equipment.courierNumber);
    if (!expressInfo) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '快递查询失败')
    return ResultData.ok(expressInfo)
  }

  /** 更新硬件 */
  async update(updateEquipmentDto: UpdateEquipmentDto, account: String, ip: String) {
    /** 查询是否存在 */
    const existing = await this.findOneById(updateEquipmentDto.id)
    if (!existing) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该硬件不存在或已删除');
    /** 更新硬件 */
    const equipmentInfo = instanceToPlain(updateEquipmentDto)
    const { affected } = await this.equipmentManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<EquipmentEntity>(EquipmentEntity, updateEquipmentDto.id, equipmentInfo)
    })

    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '更新失败，请稍后重试');
    /** 更新硬件成功后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '硬件',
      operaModule: `更新硬件 -【${updateEquipmentDto.name}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok();
  }

  /** 更新硬件录入状态 */
  async updateStatus(equipmentId: string, status: 0 | 1 | 2, auditRemark: String, account: String, ip: String): Promise<ResultData> {
    /** 查询硬件录入是否存在 */
    const existing = await this.findOneById(equipmentId)
    if (!existing) ResultData.fail(AppHttpCode.USER_NOT_FOUND, '当前硬件录入不存在或已删除')

    /** 更新硬件录入状态 */
    const { affected } = await this.equipmentManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<EquipmentEntity>(EquipmentEntity, equipmentId, { id: equipmentId, status, auditRemark })
    })
    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '更新失败，请稍后尝试');
    /** 更新硬件录入状态成功后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '硬件',
      operaModule: `更新硬件录入状态 -【${existing.name}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok()
  }

  /** 逻辑删除硬件 */
  async updateDelete(equipmentId: string, isDelete: 0 | 1, account: String, ip: String): Promise<ResultData> {
    /** 查询当前硬件是否存在 */
    const existing = await this.findOneById(equipmentId)
    if (!existing) ResultData.fail(AppHttpCode.USER_NOT_FOUND, '当前硬件不存在或已删除')
    /** 逻辑删除硬件 */
    const { affected } = await this.equipmentManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<EquipmentEntity>(EquipmentEntity, equipmentId, { id: equipmentId, isDelete })
    })
    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '删除失败，请稍后尝试');

    /** 逻辑删除硬件成功后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '硬件',
      operaModule: `删除硬件 -【${existing.name}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok()
  }
}
